///////////////////////////////////////////////////////////////////////////////
//
// Licensed under the MIT License
//
// Copyright (c) 2010 Chris Colinsky
// All Rights Reserved
//
// version	0.5
// http://code.google.com/p/sugarcookie/
// http://code.google.com/p/sugarcookie/wiki/License
//
////////////////////////////////////////////////////////////////////////////////

package sugarcookie.media {
	import caurina.transitions.Tweener;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	
	import mx.logging.ILogger;
	import mx.logging.Log;
	
	import sugarcookie.events.GenericEvent;
	
	/**
	 * Proxy class for the combination of the Sound, SoundChannel and SoundLoaderContext classes.  
	 */
	public class UIAudio extends Sprite {
		//--------------------------------------------------------------------------
	    //
	    //  Class constants
	    //
	    //--------------------------------------------------------------------------
		
		/**
		 * Defines the value of the <code>type</code> property of a <code>GenericEvent</code> event object.
		 * 
		 * <p>The properties of the event object have the following values:</p>
	     * <table class="innertable">
	     * <tr><th>Property</th><th>Value</th></tr>
	     * <tr><td><code>bubbles</code></td><td>false</td></tr>
	     * <tr><td><code>cancelable</code></td><td>false</td></tr>
	     * <tr><td><code>status</code></td><td>false</td></tr>
	     * <tr><td><code>isLoggable</code></td><td>false</td></tr>
	     * <tr><td><code>data</code></td><td>the event object that is dispatched when the ProgressEvent.PROGRESS event is received from the Sound object.</td></tr>
	     * </table>
	     *
	     * @eventType onProgress
	     * @see sugarcookie.events.GenericEvent
		 */
		public static const PROGRESS:String = "onProgress";
		
		/**
		 * Defines the value of the <code>type</code> property of a <code>GenericEvent</code> event object.
		 * 
		 * <p>The properties of the event object have the following values:</p>
	     * <table class="innertable">
	     * <tr><th>Property</th><th>Value</th></tr>
	     * <tr><td><code>bubbles</code></td><td>false</td></tr>
	     * <tr><td><code>cancelable</code></td><td>false</td></tr>
	     * <tr><td><code>status</code></td><td>false</td></tr>
	     * <tr><td><code>isLoggable</code></td><td>false</td></tr>
	     * <tr><td><code>data</code></td><td>the event object that is dispatched when position property of the SoundChannel object is updated.</td></tr>
	     * </table>
	     *
	     * @eventType currentTime
	     * @see sugarcookie.events.GenericEvent
		 */
		public static const CURRENT_TIME:String = "currentTime";
		
		/**
		 * Defines the value of the <code>type</code> property of a <code>GenericEvent</code> event object.
		 * 
		 * <p>The properties of the event object have the following values:</p>
	     * <table class="innertable">
	     * <tr><th>Property</th><th>Value</th></tr>
	     * <tr><td><code>bubbles</code></td><td>false</td></tr>
	     * <tr><td><code>cancelable</code></td><td>false</td></tr>
	     * <tr><td><code>status</code></td><td>false</td></tr>
	     * <tr><td><code>isLoggable</code></td><td>false</td></tr>
	     * <tr><td><code>data</code></td><td>the event object that is dispatched when the Event.SOUND_COMPLETE event is received from the Sound object.</td></tr>
	     * </table>
	     *
	     * @eventType onComplete
	     * @see sugarcookie.events.GenericEvent
		 */
		public static const COMPLETE:String = "onPlaybackComplete";
		
		/**
		 * Defines the value of the <code>type</code> property of a <code>GenericEvent</code> event object.
		 * 
		 * <p>The properties of the event object have the following values:</p>
	     * <table class="innertable">
	     * <tr><th>Property</th><th>Value</th></tr>
	     * <tr><td><code>bubbles</code></td><td>false</td></tr>
	     * <tr><td><code>cancelable</code></td><td>false</td></tr>
	     * <tr><td><code>status</code></td><td>false</td></tr>
	     * <tr><td><code>isLoggable</code></td><td>false</td></tr>
	     * <tr><td><code>data</code></td><td>the event object that is dispatched when the Event.COMPLETE event is received from the Sound object.</td></tr>
	     * </table>
	     *
	     * @eventType onComplete
	     * @see sugarcookie.events.GenericEvent
		 */
		public static const LOAD_COMPLETE:String = "onSoundLoadComplete";
		
		//--------------------------------------------------------------------------
	    //
	    //  Class properties
	    //
	    //--------------------------------------------------------------------------
		
		/**
		 * @private
		 */
		private var _log:ILogger;
		
		/**
		 * @private
		 */
		private var _snd:Sound;
		
		/**
		 * @private
		 */
		private var _channel:SoundChannel;
		
		/**
		 * @private
		 */
		private var _source:String;
		
		/**
		 * @private
		 */
		private var _buffer:Number;
		
		/**
		 * @private
		 */
		private var _volume:Number = 1;
		
		/**
		 * @private
		 */
		private var _pan:Number = 0;
		
		/**
		 * @private
		 */
		private var _isPlaying:Boolean = false;
		
		/**
		 * @private
		 */
		private var _lastPosition:Number = 0;
		
		//--------------------------------------------------------------------------
	    //
	    //  Constructor
	    //
	    //--------------------------------------------------------------------------
		
		/**
		 * 
		 * 
		 * @param buffer Amount of time in milliseconds to buffer the sound.
		 */
		public function UIAudio(buffer:Number=1000) {
			super();
			
			_log = Log.getLogger("sugarcookie.media.UIAudio");
			_buffer = buffer;
		}
		
		public final function load(audio_uri:String):void {
			_snd = new Sound();
			_snd.addEventListener(Event.ID3, id3Handler,false,0,true);
			_snd.addEventListener(IOErrorEvent.IO_ERROR, errorHandler,false,0,true);
			_snd.addEventListener(Event.COMPLETE, completeHandler,false,0,true);
            _snd.addEventListener(ProgressEvent.PROGRESS, progressHandler,false,0,true);
			
			_source = audio_uri;
			try {
            	_snd.load(new URLRequest(audio_uri), new SoundLoaderContext(_buffer, false));
            } catch (err:Error) {
                _log.error(err.message);
            }
		}
		
		public final function get source():String {
			return _source;
		}
		
		public final function get isPlaying():Boolean {
			return _isPlaying;
		}
		
		public final function get length():Number {
			return _snd!=null?_snd.length:0;
		}
		
		public final function get isLoaded():Boolean {
			return !_snd.hasEventListener(Event.COMPLETE);
		}
		
		public function set pan(pan:Number):void {
            if (_channel==null) return;
            
            var transform:SoundTransform = _channel.soundTransform;
            _pan = transform.pan = pan;
            _channel.soundTransform = transform;
        }
        
        public function get pan():Number {
			return _channel.soundTransform.pan;
		}
		
		public function set volume(vol:Number):void {
			if (_channel==null) {
				_volume = vol;
				return;
			}
			
			var transform:SoundTransform = _channel.soundTransform;
            _volume = transform.volume = vol;
            _channel.soundTransform = transform;
		}
		
		public function get volume():Number {
			return _channel.soundTransform.volume;
		}
		
		public final function play(startTime:Number=0,loops:int=0,sndTransform:SoundTransform=null):void {
			_isPlaying = true;
			 addEventListener(Event.ENTER_FRAME,onEnterFrame,false,0,true);
			_channel = _snd.play(startTime,loops,sndTransform);
			volume = _volume;
			pan = _pan;
			_channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler,false,0,true);
		}
		
		public final function resume(newTime:Number=0):void {
			_isPlaying = true;
			if (newTime==0) newTime = _lastPosition;
			 addEventListener(Event.ENTER_FRAME,onEnterFrame,false,0,true);
			_channel = _snd.play(newTime);
			volume = _volume;
			pan = _pan;
			_channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler,false,0,true);
		}
		
		public final function pause():void {
			_isPlaying = false;
			 removeEventListener(Event.ENTER_FRAME,onEnterFrame);
			 _lastPosition = _channel.position;
			_channel.stop();
			_channel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
		}
		
		public final function close():void {
			_isPlaying = false;
			_source = null;
			_lastPosition = 0;
			removeEventListener(Event.ENTER_FRAME,onEnterFrame);
			if (_snd!=null) {
				if (_snd.hasEventListener(Event.ID3)) _snd.removeEventListener(Event.ID3, id3Handler);
				if (_snd.hasEventListener(IOErrorEvent.IO_ERROR)) _snd.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
				if (_snd.hasEventListener(Event.COMPLETE)) _snd.removeEventListener(Event.COMPLETE, completeHandler);
	            if (_snd.hasEventListener(ProgressEvent.PROGRESS)) _snd.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
	            try {
	            	_snd.close();
	            } catch (err:Error) {
	            	if (_channel!=null) _channel.stop();
	            }
	   		}	
		}
		
		public final function seek(offset:Number=0):void {
			_snd.play(offset);
		}
		
		protected function onEnterFrame(evt:Event):void {
			var p:Number = _channel.position / _snd.length;
			dispatchEvent(new GenericEvent(UIAudio.CURRENT_TIME,false,false,false,false,{percent:p,position:_channel.position,length:_snd.length}));
		}
		
		protected function soundCompleteHandler(evt:Event):void {
			_channel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
			_log.debug("sound complete: "+evt.toString());
			dispatchEvent(new GenericEvent(UIAudio.COMPLETE,false,false,false,false));
		}
		
		protected function id3Handler(evt:Event):void {
			_log.debug("ID3 info: "+evt.toString());
		}
		
		protected function errorHandler(evt:IOErrorEvent):void {
			_log.error("The sound could not be loaded: "+evt.text);
		}
		
		protected function progressHandler(evt:ProgressEvent):void {
			var perc:Number = evt.bytesLoaded / evt.bytesTotal;
            dispatchEvent(new GenericEvent(UIAudio.PROGRESS,false,false,false,false,{percent:perc}));
		}
		
		protected function completeHandler(evt:Event):void {
			_log.debug("complete: "+evt.toString());
			_snd.removeEventListener(Event.COMPLETE, completeHandler);
            _snd.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
            dispatchEvent(new GenericEvent(UIAudio.LOAD_COMPLETE,false,false,false,false));
		}
	}
}